#include "memory.h"
#include "stdint.h"
#include "print.h"

#define PG_SIZE 4096

#define MEM_BITMAP_BASE 0xc009a000

/* 
0xc0000000是内核从虚拟地址3G起
0x100000意指跨过低端1MB内存，是虚拟地址在逻辑上连续
*/
#define K_HEAP_START 0xc0100000

#define PDE_IDX(addr) ((addr & 0xffc00000) >>22)
#define PTE_IDX(addr) ((addr & 0x003ff000) >>12)

//物理内存池结构体，用于管理物理内存池
struct pool{
	//pool_bitmap和phy_addr_start是同一个地址吗？
	struct bitmap pool_bitmap;
	uint32_t phy_addr_start;
	uint32_t pool_size;
}
//生成两个实例用于管理内核物理内存池和用户物理内存池
struct pool kernel_pool, user_pool;
//管理内核虚拟地址的实例
struct virtual_addr kernel_vaddr;

static void mem_pool_init(uint32_t all_mem){
	put_str(" mem_pool_init start\n");
	uint32_t page_table_size = PG_SIZE * 256;
	uint32_t used_mem = page_table_size + 0x100000;
	uint32_t free_mem = all_mem - used_mem;
	uint16_t all_free_pages = free_mem / PG_SIZE;
	
	uint16_t kernel_free_pages = all_free_pages / 2;
	uint16_t user_free_pages = all_free_pages - kernel_free_pages;
	
	//为何要除以8？位图的长度指的是什么？指的是bytes的个数
	uint32_t kbm_length = kernel_free_pages / 8;
	uint32_t ubm_length = user_free_pages / 8;
	
	uint32_t kp_start = used_mem;
	uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE;
	kernel_pool.phy_addr_start = kp_start;
	user_pool.phy_addr_start = up_start;
	kernel_pool.pool_size = kernel_free_pages * PG_SIZE;
	user_pool.pool_size = user_free_pages *PG_SIZE;
	kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;
	user_pool.pool_bitmap.btmp_bytes_len = ubm_length;

    kernel_pool.pool_bitmap.bits = (void*)MEM_BITMAP_BASE;
    user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length);

    put_str(" kernel_pool_bitmap_start:");
    put_int((int)kernel_pool.pool_bitmap.bits);
    put_str(" kernel_pool_phy_addr_start:");
    put_int((kernel_pool.phy_addr_start);
    put_str("\n")
    put_str(" user_pool_bitmap_start:");
    put_int(int)user_pool.pool_bitmap.bits);
    put_str(" user_pool_phy_addr_start:");
    put_int((user_pool.phy_addr_start);
    put_str("\n")

    /*将位图置0*/
    bitmap_init(&kernel_pool.pool_bitmap);
    bitmap_init(&user_pool.pool_bitmap);

    kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;
    kernel_vaddr.vaddr_bitmap.bits = (void*) (MEM_BITMAP_BASE + kbm_length + ubm_length);
    kernel_vaddr.vaddr_start = K_HEAP_START;
    bitmap_init(&kernel_vaddr.vaddr_bitmap);
    put_str(" mem_pool_init done\n");
}

/*
 * 在虚拟内存池中申请pg_cnt个虚拟页
 * 成功则返回虚拟页的起始地址，失败则返回NULL
 */
static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt){
    int vaddr_start = 0, bit_idx_start = -1;
    uint32_t cnt = 0;
    if(pf == PF_KERNEL){
        bit_idx_start = bitmap_scan(&kernel_vaddr.vaddr_bitmap, pg_cnt);
        if(bit_idx_start == -1){
            return NULL;
        }
        while(cnt<pg_cnt){
            bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1);
        }
        //由此可见，bitmap和虚拟起始地址是如何协作管理虚拟内存的
        vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE;
    }
}

//得到虚拟地址对应的pte指针
//pte(Page Table Entry)页表项，指的是页表中的一条项，而不是页表本身
uint32_t* pte_ptr(uint32_t vaddr){
    uint32_t pte_ptr = (uint32_t*)(0xffc00000 + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4);
    return pte_ptr;
}

//得到虚拟地址对应的pde指针
//pde(Page Directory Entry)页目录项。指的是页目录表中的一条项，而不是页目录表本身
/*
 * 由于要访问的是vaddr所在的页目录项pde，所以必须想办法在第2步（二级分页索引）的时候让处理器pte索引时获得的是页目录表物理地址
 * 页目录项的最后一项中存储的是页目录表物理地址。
 * 0xfffffxxx的高10位是0x3ff,中间10位也是0x3ff，也就是说，CPU在处理该地址时，先用前10位索引得到页目录表的物理地址
 * 然后在此地址基础上再用中间10位索引，又一次得到了页目录表的物理地址。
 */
uint32_t* pde_ptr(uint32_t vaddr){
    uint32_t* pde_ptr = (uint32_t*)((0xfffff000) + PDE_IDX(vaddr) * 4);
    return pde_ptr;
}

/*
 * 在m_pool指向的物理内存池中分配1个物理页
 * 成功则返回页框的物理地址，失败则返回NULL
 */
static void* palloc(struct pool* m_pool){
    int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1);   //找一个物理页面
    if(bit_idx == -1){
        return NULL;
    }
    bitmap_set(&m_pool->pool_bitmap, bit_idx, 1);
    uint32_t page_phyaddr = ((bit_idx * PG_SIZE) + m_pool->phy_addr_start);   //bitmap的索引和物理地址是一一对应的
    return (void*)page_phyaddr;
}

/*
 * 页表中添加虚拟地址_vaddr与物理地址_page_phyaddr的映射
 */
static void page_table_add(void* _vaddr, void* _page_phyaddr){
    uint32_t vaddr = (uint32_t)_vaddr, page_phyaddr = (uint32_t)_page_phyaddr;
    uint32_t* pde = pde_ptr(vaddr);
    uint32_t* pte = pte_ptr(vaddr);
    if(*pde & 0x00000001){  //现在页目录内判断目录项的P位，若为1，则表示该表以存在
        ASSERT(!(*pte & 0x00000001));
        if(!(*pte & 0x00000001)){
            *pte =
        }
    }

}

void mem_init(){
    put_str(" mem_pool_init start\n");
    uint32_t mem_bytes_total = (*(uint32_t*)(0xb00));
    mem_pool_init(mem_bytes_total);
    put_str("mem_init done\n");
}